#include "mysql_conn_pool.h"

MySqlConnPool::MySqlConnPool() {
    m_freeConns = 0;
    m_busyConns = 0;
}

MySqlConnPool::~MySqlConnPool() {
    close_pool();
}

MySqlConnPool *MySqlConnPool::get_instance() {
    static MySqlConnPool connPool;
    return &connPool;
}

void MySqlConnPool::init(const char *url, uint32_t port, const char *name,
                         const char *uname, const char *passwd, uint32_t max_conns) {

    // 创建访问连接
    for (int i = 0; i < max_conns; ++i) {
        MYSQL *new_conn = nullptr;

        new_conn = mysql_init(new_conn);
        if (new_conn == nullptr) {
            cerr << "MySQL init Error." << endl;
            exit(1);
        }

        new_conn = mysql_real_connect(new_conn, url, uname, passwd, name, port, nullptr, 0);
        if (new_conn == nullptr) {
            cerr << "MySQL Error." << endl;
            exit(1);
        }

        m_connQueue.push(new_conn);
        ++m_freeConns;
    }

    // 初始化连接信号量
    m_sem_reserve = Semaphore(m_freeConns);
}

MYSQL *MySqlConnPool::get_connection() {
//    if (m_connQueue.empty()) {
//        return nullptr;
//    }

    m_sem_reserve.wait();    // 信号量
    m_locker.lock();

    MYSQL *conn = m_connQueue.front();
    m_connQueue.pop();
    --m_freeConns;
    ++m_busyConns;

    m_locker.unlock();

    return conn;
}

bool MySqlConnPool::release_connection(MYSQL *conn) {
    if (conn == nullptr)  return false;

    m_locker.lock();

    m_connQueue.push(conn);
    ++m_freeConns;
    --m_busyConns;

    m_locker.unlock();
    m_sem_reserve.post();    // 释放信号量

    return true;
}

void MySqlConnPool::close_pool() {
    m_locker.lock();

    while (!m_connQueue.empty()) {
        mysql_close(m_connQueue.front());
        m_connQueue.pop();
    }
    m_busyConns = 0;
    m_freeConns = 0;

    m_locker.unlock();
}

int MySqlConnPool::get_free_num() {
    m_locker.lock();
    int busy_num = m_busyConns;
    m_locker.unlock();
    return busy_num;
}


MySqlConn::MySqlConn(MYSQL **sqlConn) {
    init_raii();
    *sqlConn = m_sqlConn;
}

MySqlConn::MySqlConn() {
    init_raii();
}

MySqlConn::~MySqlConn() {
    // RAII
    m_sqlPool->release_connection(m_sqlConn);
    LOG_DEBUG("A MySql connection has been released.");
}

MYSQL *MySqlConn::get() const {
    return m_sqlConn;
}

void MySqlConn::init_raii() {
    // RAII
    m_sqlPool = MySqlConnPool::get_instance();
    m_sqlConn = m_sqlPool ->get_connection();
}


